package com.fsgom.common.web.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

public final class CookieUtils {
	public static final String COOKIE_DEFAULT_PATH = "/";
	public static final int COOKIE_DEFAULT_MAX_AGE = -1;
	public static final int COOKIE_DEFAULT_MIN_AGE = 0;
	private static final String[] COOKIE_ENCODINGS = new String[] {"8859_1", "UTF-8"};

	private CookieUtils() {
		throw new UnsupportedOperationException();
	}

	public static String encode(String string) {
		try {
			return URLEncoder.encode(string, COOKIE_ENCODINGS[0]);
		} catch (UnsupportedEncodingException e) {
			return string;
		}
	}

	public static String decode(String string) {
		try {
			return URLDecoder.decode(string, COOKIE_ENCODINGS[1]);
		} catch (UnsupportedEncodingException e) {
			return string;
		}
	}

	/**
	 * {@link Cookie}를 생성한다.
	 * 
	 * @param name 이름
	 * @param value 값
	 * @param maxAge 쿠키의 최대 수명(단위:초)
	 * @param domain 쿠키가 표시될 도메인
	 * @param path 클라이언트가 쿠키를 반환해야 할 경로
	 */
	public static Cookie createCookie(String name, String value, int maxAge, String domain, String path) {
		Cookie cookie = new Cookie(name, value);

		if (domain != null) {
			cookie.setDomain(domain);
		}

		cookie.setMaxAge(maxAge);
		cookie.setPath(path);

		return cookie;
	}

	/**
	 * {@link Cookie}를 생성한다.
	 * 
	 * @param name 이름
	 * @param value 값
	 * @param maxAge 쿠키의 최대 수명(단위:초)
	 * @param domain 쿠키가 표시될 도메인
	 */
	public static Cookie createCookie(String name, String value, int maxAge, String domain) {
		return createCookie(name, value, maxAge, domain, COOKIE_DEFAULT_PATH);
	}

	/**
	 * {@link Cookie}를 생성한 후 {@link HttpServletResponse}에 저장한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param name 이름
	 * @param value 값
	 * @param maxAge 쿠키의 최대 수명(단위:초)
	 * @param domain 쿠키가 표시될 도메인
	 * @param path 클라이언트가 쿠키를 반환해야 할 경로
	 */
	public static Cookie setCookie(HttpServletResponse response, String name, String value, int maxAge, String domain, String path) {
		Cookie cookie = createCookie(name, value, maxAge, domain, path);
		response.addCookie(cookie);
		return cookie;
	}

	/**
	 * {@link Cookie}를 생성한 후 {@link HttpServletResponse}에 저장한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param name 이름
	 * @param value 값
	 * @param maxAge 쿠키의 최대 수명(단위:초)
	 * @param domain 쿠키가 표시될 도메인
	 */
	public static Cookie setCookie(HttpServletResponse response, String name, String value, int maxAge, String domain) {
		return setCookie(response, name, value, maxAge, domain, COOKIE_DEFAULT_PATH);
	}

	/**
	 * {@link Cookie}를 생성한 후 {@link HttpServletResponse}에 저장한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param cookie {@link Cookie} 객체
	 */
	public static Cookie setCookie(HttpServletResponse response, Cookie cookie) {
		response.addCookie(cookie);
		return cookie;
	}

	/**
	 * {@link HttpServletResponse}에 저장된 {@link Cookie}를 반환한다.
	 * 
	 * @param request {@link HttpServletRequest} 객체
	 * @param name 쿠키 이름
	 */
	public static Cookie getCookie(HttpServletRequest request, String name) {
		if (StringUtils.isEmpty(name)) {
			return null;
		}

		Cookie cookies[] = request.getCookies();

		if (cookies == null) {
			return null;
		}

		for (Cookie cookie : cookies) {
			if (cookie.getName().equals(name)) {
				return cookie;
			}
		}

		return null;
	}

	/**
	 * {@link HttpServletRequest}에 해당 이름에 대한 {@link Cookie}의 값을 반환한다.
	 * 
	 * @param request {@link HttpServletRequest} 객체
	 * @param name 쿠키 이름
	 */
	public static String getCookieValue(HttpServletRequest request, String name) {
		Cookie cookie = getCookie(request, name);
		return cookie == null ? null : cookie.getValue();
	}

	/**
	 * {@link HttpServletRequest}에 저장된 모든 {@link Cookie} 정보를 반환한다.
	 * 
	 * @param request {@link HttpServletRequest} 객체
	 * @return {@link Cookie} 정보
	 */
	public static Map<String, String> getCookieValues(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		Map<String, String> cookieMap = new HashMap<String, String>();

		if (cookies == null) {
			return cookieMap;
		}

		for (Cookie cookie : cookies) {
			cookieMap.put(cookie.getName(), cookie.getValue());
		}

		return cookieMap;
	}

	/**
	 * {@link HttpServletRequest}에 저장된 모든 {@link Cookie}의 이름을 반환한다.
	 * 
	 * @param request {@link HttpServletRequest} 객체
	 */
	public static String[] getCookieNames(HttpServletRequest request) {
		List<String> cookieNames = new ArrayList<String>();
		Cookie[] cookies = request.getCookies();

		if (cookies != null) {
			for (Cookie cookie : cookies) {
				cookieNames.add(cookie.getName());
			}
		}

		return cookieNames.toArray(new String[cookieNames.size()]);
	}

	/**
	 * {@link HttpServletRequest}에 저장된 {@link Cookie} 중에 <code>filter</code>로 시작하는 이름을 가진 {@link Cookie}의 이름을 반환한다.
	 * 
	 * @param response {@link HttpServletRequest} 객체
	 */
	public static String[] getCookieNames(HttpServletRequest request, String filter) {
		List<String> cookieNames = new ArrayList<String>();
		Cookie[] cookies = request.getCookies();

		if (cookies != null) {
			for (Cookie cookie : cookies) {
				String cookieName = cookie.getName();

				if (cookieName.indexOf(filter) != -1) {
					cookieNames.add(cookieName);
				}
			}
		}

		return cookieNames.toArray(new String[cookieNames.size()]);
	}

	/**
	 * {@link HttpServletRequest}에 지정된 이름을 가진 {@link Cookie}가 있는지 확인한다.
	 * 
	 * @param cookieName 쿠키 이름.
	 */
	public static boolean isExistCookie(HttpServletRequest request, String cookieName) {
		boolean exists = false;
		Cookie[] cookies = request.getCookies();

		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (cookie.getName().equals(cookieName)) {
					exists = true;
					break;
				}
			}
		}

		return exists;
	}

	/**
	 * {@link HttpServletResponse}에 저장된 {@link Cookie}를 제거한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param name 이름
	 * @param domain 쿠키가 표시될 도메인
	 * @param path 클라이언트가 쿠키를 반환해야 할 경로
	 */
	public static void invalidateCookie(HttpServletResponse response, String cookieName, String domain, String path) {
		setCookie(response, cookieName, null, COOKIE_DEFAULT_MIN_AGE, domain, path);
	}

	/**
	 * {@link HttpServletResponse}에 저장된 {@link Cookie}를 제거한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param cookieName 쿠키 이름
	 * @param domain 쿠키가 표시될 도메인
	 */
	public static void invalidateCookie(HttpServletResponse response, String cookieName, String domain) {
		invalidateCookie(response, cookieName, domain, COOKIE_DEFAULT_PATH);
	}

	/**
	 * {@link HttpServletResponse}에 저장된 {@link Cookie}를 제거한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param cookieName 쿠키 이름
	 */
	public static void invalidateCookie(HttpServletResponse response, String cookieName) {
		invalidateCookie(response, cookieName, null, COOKIE_DEFAULT_PATH);
	}

	/**
	 * {@link HttpServletResponse}에 저장된 {@link Cookie}를 제거한다.
	 * 
	 * @param response {@link HttpServletResponse} 객체
	 * @param cookieNames 쿠키 이름들
	 */
	public static void invalidateCookies(HttpServletResponse response, String... cookieNames) {
		if (!ArrayUtils.isEmpty(cookieNames)) {
			return;
		}

		for (String cookieName : cookieNames) {
			invalidateCookie(response, cookieName);
		}
	}

	/**
	 * {@link HttpServletResponse}에 저장된 해당 도메인의 모든 {@link Cookie}를 제거한다.
	 * 
	 * @param request {@link HttpServletRequest} 객체
	 * @param response {@link HttpServletResponse} 객체
	 * @param cookieDomain 쿠키가 표시될 도메인
	 */
	public static void invalidateAllCookies(HttpServletRequest request, HttpServletResponse response, String cookieDomain) {
		Cookie[] cookies = request.getCookies();

		if (!ArrayUtils.isEmpty(cookies)) {
			for (Cookie cookie : cookies) {
				invalidateCookie(response, cookie.getName(), cookieDomain);
			}
		}
	}
}
